import Watcher from "../watcher.js";

// 把数据挂载到页面上
export default function mount(vm) {
  // 获取根节点
  let { el } = vm.$options;
  el = document.querySelectorAll(el);
  // el[0].childNodes 默认情况下是 NodeList类数组,输出可看,不能直接进行遍历需要转换为普通数组
  // 把vm实例传下去 方便替换数据
  compuleNode(Array.from(el[0].childNodes), vm);
}

// 查找变量并替换
function compuleNode(nodes, vm) {
  // node 是我们拿到的所有根节点下的dom
  // 遍历dom节点
  for (const node of nodes) {
    // console.log('node ===> forin ', node, node.nodeType);
    // 节点有区别:
    // nodeType === 1 表示是元素div span这些元素等
    // nodeType === 3 表示是文本 也就是最后一级
    if (node.nodeType === 3) {
      compuleTextNode(node, vm);
    }
    // 如果是dom元素元素
    // 1.解析v-bind v-model v-on:click
    // 2.递归遍历查找他的子元素
    if (node.nodeType === 1) {
      // console.log('node ==> ', node)
      compileAttribute(node, vm);
      compuleNode(node.childNodes, vm);
    }
  }
}

// 替换数据 支持多个数据
function compuleTextNode(node, vm) {
  // 获取匹配的结果
  // 替换dom得值
  // 写一个回调 专门修改当前这个节点的数据
  // 缓存content 因为cb替换后就没有了
  const contentCache = node.textContent;
  function cb() {
    // 取出 {{ 引用的变量 }}
    const braceRegx = /({{.*?}})/g;
    // 可变动的
    let TContent = contentCache;
    const getBrace = TContent.match(braceRegx);
    for (const keyItem in getBrace) {
      // 获取每一项 {{name}}  or {{age}}
      const keyBrace = getBrace[keyItem].replace(/\s*/g, "");
      // 获取花括号下的key
      const keyData = keyBrace.match(/{{(.*?)}}/);
      // 替换成真实的数据
      TContent = TContent.replace(getBrace[keyItem], vm[keyData[1]]);
    }
    node.textContent = TContent || `${key} 未定义`;
  }
  // 订阅后即可支持动态修改
  new Watcher(cb);
}

// 调用节点中的方法
function compileAttribute(node, vm) {
  const attrs = Array.from(node.attributes);
  for (const attr of attrs) {
    const { name, value } = attr;
    if (name.match(/v-on:click/) || name.match(/@click/)) {
      // <button v-on:click="xx">
      compilerOn(node, value, vm);
    } else if (name.match(/v-bind:/)) {
      // <span v-bind:class="xx">
      compilerBind(node, name, value, vm);
    } else if (name.match(/v-model/) || name.match(/:model/)) {
      // <input v-model:value="xx" or :model>
      compilerModel(node, value, vm);
    }
  }
}

// 处理On 事件
function compilerOn(node, methdo, vm) {
  // console.log("methdo ==> ", methdo);
  // methdo 有两种格式 printData(name) 或者 printData
  // 解析出来key
  const regx = /\(.*\)$/; // 找到第一个 '(' 的位置
  if (methdo) {
    const e = methdo.match(regx, "");
    methdo = methdo.slice(0, e.index);
  }
  // 添加on事件
  node.addEventListener("click", function (...args) {
    // apply 以后,我们的方法体内的this就指向了我们vm实例本身
    vm.$options.methods[methdo].apply(vm, args);
  });
}

// 处理 compilerModel 双向绑定
function compilerModel(node, key, vm) {
  let { type, tagName } = node;
  let update;
  tagName = tagName.toLowerCase();
  // 绑定在了Input上
  if (tagName === "input" && type === "text") {
    node.addEventListener("input", function (e) {
      vm[key] = node.value;
    });
    update = () => {
      node.value = vm[key];
    };
    // 将当前标签订阅到到参数上
    new Watcher(update);
    // 绑定在了Checkbox上
  } else if (tagName === "input" && type === "checkbox") {
    node.addEventListener("change", function (e) {
      vm[key] = node.checked;
    });
    update = () => {
      node.checked = vm[key];
    };
    // 将当前标签订阅到到参数上
    new Watcher(update);
  } else if (tagName === "select") {
    node.addEventListener("input", function (e) {
      vm[key] = node.value;
    });
    update = () => {
      node.value = vm[key];
    };
  }

  // 将当前标签订阅到到参数上
  // 解释: 我们在Watcher初始化时会执行update
  // 也就会触发我们的data的get参数,此时target指向当前执行的Watcher,完成关联和依赖收集
  new Watcher(update);
}

// 处理Bind
function compilerBind(node, attrName, attrValue, vm) {
  // 移除 v-bind:title 因为浏览器不支持这种格式
  node.removeAttribute(attrName);
  // 获取到 title
  attrName = attrName.replace("v-bind:", "");
  const update = () => {
    // 设置title属性
    node.setAttribute(attrName, vm[attrValue]);
  };
  // 监听一下,如果改变会重新执行
  new Watcher(update);
}
